home *** CD-ROM | disk | FTP | other *** search
- ; Project #2, Chapter five
- ;
- ; AMAZE.ASM
- ;
- ; A maze generation/solution program.
- ;
- ; This program generates an 80x25 maze and directly draws the maze on the
- ; video display.
- ;
- ; You need to supply some code to access the 80x25 cells of the maze on the
- ; video display. For full details on how this program works, see the chapter
- ; on processes in the textbook. Warning! This program is relatively complex.
- ; Don't try to understand how it works if you're working on this after
- ; just reading chapter five
- ;
- ; When this program runs it will generate a maze and then pause until you
- ; print a key. Then it will solve the maze and stop on the next keypress.
- ;
- ; This version will only work on a color display.
-
-
-
-
- cseg segment para public 'code'
- assume cs:cseg, ds:dseg
-
-
- ; MazeAdrs computes the index into the maze array.
- ; Maze is a 27x82 array of words (maze:array[0..26,0..81] of word).
- ; The following procedure needs to compute the index into this array
- ; and return that index in the AX register. On input, dx contains
- ; the X coordinate (0..81) and cx contains the Y coordinate (0..26).
- ; Each element of the array is a word.
- ;
-
- MazeAdrs textequ <call DoMazeAdrs>
-
- DoMazeAdrs proc
- push bx
- push cx
- push dx
-
- mov cl, dh
- mov dh, 0
- mov ch, 0
- xchg cx, dx
-
- ; Do your computations here:
-
-
-
- ; Complete your computations by this point.
-
- pop dx
- pop cx
- pop bx
- ret
- DoMazeAdrs endp
-
-
- ; The following procedure needs to be just like the code above
- ; except the matrix it computes the index for is a 25x80 matrix,
- ; not a 27x82 matrix. Once again, the X coordinate (0..79) is in
- ; CX and the Y coordinate (0..24) is in DX. Use row major ordering.
- ; You may use the AX, BX, CX, and DX registers. Return the index
- ; in the AX register.
-
- ScrnAdrs textequ <call DoScrnAdrs>
-
- DoScrnAdrs proc
- push bx
- push cx
- push dx
-
- mov cl, dh
- mov ch, 0
- mov dh, 0
- dec dx
- dec cx
- xchg cx, dx
-
-
- ; Do your computations here.
-
-
- ; Complete your computations by this point.
-
- pop dx
- pop cx
- pop bx
- ret
- DoScrnAdrs endp
-
-
-
- cseg ends
-
-
-
-
- ; ***** Don't mess with anything beyond this point !!! ********
-
-
-
-
-
-
- .xlist
- include stdlib.a
- includelib stdlib.lib
- .list
-
- byp textequ <byte ptr>
-
- dseg segment para public 'data'
-
- ; Constants:
- ;
- ; Define the "ToScreen" symbol (to any value) if the maze is 80x25 and you
- ; want to display it on the video screen.
-
- ToScreen equ 0
-
-
- ; Maximum X and Y coordinates for the maze (matching the display).
-
- MaxXCoord equ 80
- MaxYCoord equ 25
-
- ; Useful X,Y constants:
-
- WordsPerRow = MaxXCoord+2
- BytesPerRow = WordsPerRow*2
-
- StartX equ 1 ;Starting X coordinate for maze
- StartY equ 3 ;Starting Y coordinate for maze
- EndX equ MaxXCoord ;Ending X coordinate for maze
- EndY equ MaxYCoord-1 ;Ending Y coordinate for maze
-
- EndLoc = ( (EndY-1)*MaxXCoord + EndX-1)*2
- StartLoc = ( (StartY-1)*MaxXCoord + StartX-1)*2
-
- ; Special 16-bit PC character codes for the screen for symbols drawn during
- ; maze generation. See the chapter on the video display for details.
-
- ifdef mono ;Mono display adapter.
-
- WallChar equ 7dbh ;Solid block character
- NoWallChar equ 720h ;space
- VisitChar equ 72eh ;Period
- PathChar equ 72ah ;Asterisk
-
- else ;Color display adapter.
-
- WallChar equ 1dbh ;Solid block character
- NoWallChar equ 0edbh ;space
- VisitChar equ 0bdbh ;Period
- PathChar equ 4e2ah ;Asterisk
-
- endif
-
-
-
-
- ; The following are the constants that may appear in the Maze array:
-
- Wall = 0
- NoWall = 1
- Visited = 2
-
- ; The following are the directions the demons can go in the maze
-
- North = 0
- South = 1
- East = 2
- West = 3
-
-
- ; Some important variables:
-
-
- ; The Maze array must contain an extra row and column around the
- ; outside edges for our algorithm to work properly.
-
- Maze word (MaxYCoord+2) dup ((MaxXCoord+2) dup (Wall))
-
-
-
- ; PCB for the main program. The last live demon will call this guy when
- ; it dies.
-
- MainPCB pcb {}
-
-
- ; List of up to 32 demons.
-
- MaxDemons = 32 ;Must be a power of two.
- ModDemons = MaxDemons-1 ;Mask for MOD computation.
-
- DemonList pcb MaxDemons dup ({})
-
- DemonIndex byte 0 ;Index into demon list.
- DemonCnt byte 0 ;Number of demons in list.
-
-
- ; Random number generator seed (we'll use our random number generator
- ; rather than the standard library's because we want to be able to specify
- ; an initial seed value).
-
- Seed word 0
-
- dseg ends
-
-
-
- ; The following is the segment address of the video display, change this
- ; from 0B800h to 0B000h if you have a monochrome display rather than a
- ; color display.
-
- ScreenSeg segment at 0b800h
- Screen equ this word ;Don't generate in date here!
- ScreenSeg ends
-
-
- cseg segment para public 'code'
- assume cs:cseg, ds:dseg
-
- ; Totally bogus random number generator, but we don't need a really
- ; great one for this program. This code uses its own random number
- ; generator rather than the one in the Standard Library so we can
- ; allow the user to use a fixed seed to produce the same maze (with
- ; the same seed) or different mazes (by choosing different seeds).
-
- RandNum proc near
- push cx
- mov cl, byte ptr Seed
- and cl, 7
- add cl, 4
- mov ax, Seed
- xor ax, 55aah
- rol ax, cl
- xor ax, Seed
- inc ax
- mov Seed, ax
- pop cx
- ret
- RandNum endp
-
- ; Init- Handles all the initialization chores for the main program.
- ; In particular, it initializes the coroutine package, gets a
- ; random number seed from the user, and initializes the video display.
-
- Init proc near
- print
- byte "Enter a small integer for a random number seed:",0
- getsm
- atoi
- free
- mov Seed, ax
-
- ; Fill the interior of the maze with wall characters, fill the outside
- ; two rows and columns with nowall values. This will prevent the demons
- ; from wandering outside the maze.
-
-
- ; Fill the first row with Visited values.
-
- cld
- mov cx, WordsPerRow
- lesi Maze
- mov ax, Visited
- rep stosw
-
- ; Fill the last row with NoWall values.
-
- mov cx, WordsPerRow
- lea di, Maze+(MaxYCoord+1)*BytesPerRow
- rep stosw
-
- ; Write a NoWall value to the starting position:
-
- mov Maze+(StartY*WordsPerRow+StartX)*2, NoWall
-
-
- ; Write NoWall values along the two vertical edges of the maze.
-
- lesi Maze
- mov cx, MaxYCoord+1
- EdgesLoop: mov es:[di], ax ;Plug the left edge.
- mov es:[di+BytesPerRow-2], ax ;Plug the right edge.
- add di, BytesPerRow
- loop EdgesLoop
-
-
- ifdef ToScreen
-
- ; Okay, fill the screen with WallChar values:
-
- lesi Screen
- mov ax, WallChar
- mov cx, 2000
- rep stosw
-
- ; Write appropriate characters to the starting and ending locations:
-
- mov word ptr es:Screen+EndLoc, PathChar
- mov word ptr es:Screen+StartLoc, NoWallChar
-
- endif ;ToScreen
-
-
- ; Zero out the DemonList:
-
- mov cx, (size pcb)*MaxDemons
- lea di, DemonList
- mov ax, dseg
- mov es, ax
- xor ax, ax
- rep stosb
-
- ret
- Init endp
-
-
-
- ; CanStart- This function checks around the current position
- ; to see if the maze generator can start digging a new tunnel
- ; in a direction perpendicular to the current tunnel. You can
- ; only start a new tunnel if there are wall characters for at
- ; least two positions in the desired direction:
- ;
- ; ##
- ; *##
- ; ##
- ;
- ; If "*" is current position and "#" represent wall characters
- ; and the current direction is north or south, then it is okay
- ; for the maze generator to start a new path in the east dir-
- ; ection. Assuming "." represents a tunnel, you cannot start
- ; a new tunnel in the east direction if any of the following
- ; patterns occur:
- ;
- ; .# #. ## ## ## ##
- ; *## *## *.# *#. *## *##
- ; ## ## ## ## .# #.
- ;
- ; CanStart returns true (carry set) if we can start a new tunnel off the
- ; path being dug by the current demon.
- ;
- ; On entry, dl is demon's X-Coordinate
- ; dh is demon's Y-Coordinate
- ; cl is demon's direction
-
- CanStart proc near
- push ax
- push bx
-
- MazeAdrs ;Compute index to demon(x,y) in maze.
- mov bx, ax
-
- ; CL contains the current direction, 0=north, 1=south, 2=east, 3=west.
- ; Note that we can test bit #1 for north/south (0) or east/west (1).
-
- test cl, 10b ;See if north/south or east/west
- jz NorthSouth
-
- ; If the demon is going in an east or west direction, we can start a new
- ; tunnel if there are six wall blocks just above or below the current demon.
- ; Note: We are checking if all values in these six blocks are Wall values.
- ; This code depends on the fact that Wall characters are zero and the sum
- ; of these six blocks will be zero if a move is possible.
-
- mov al, byp Maze[bx+BytesPerRow*2] ;Maze[x, y+2]
- add al, byp Maze[bx+BytesPerRow*2+2] ;Maze[x+1,y+2]
- add al, byp Maze[bx+BytesPerRow*2-2] ;Maze[x-1,y+2]
- je ReturnTrue
-
- mov al, byp Maze[bx-BytesPerRow*2] ;Maze[x, y-2]
- add al, byp Maze[bx-BytesPerRow*2+2] ;Maze[x+1,y-2]
- add al, byp Maze[bx-BytesPerRow*2-2] ;Maze[x-1,y-2]
- je ReturnTrue
-
- ReturnFalse: clc ;Clear carry = false.
- pop bx
- pop ax
- ret
-
- ; If the demon is going in a north or south direction, we can start a
- ; new tunnel if there are six wall blocks just to the left or right
- ; of the current demon.
-
- NorthSouth: mov al, byp Maze[bx+4] ;Maze[x+2,y]
- add al, byp Maze[bx+BytesPerRow+4] ;Maze[x+2,y+1]
- add al, byp Maze[bx-BytesPerRow+4] ;Maze[x+2,y-1]
- je ReturnTrue
-
- mov al, byp Maze[bx-4] ;Maze[x-2,y]
- add al, byp Maze[bx+BytesPerRow-4] ;Maze[x-2,y+1]
- add al, byp Maze[bx-BytesPerRow-4] ;Maze[x-2,y-1]
- jne ReturnFalse
-
- ReturnTrue: stc ;Set carry = true.
- pop bx
- pop ax
- ret
- CanStart endp
-
-
-
-
- ; CanMove- Tests to see if the current demon (dir=cl, x=dl, y=dh) can
- ; move in the specified direction. Movement is possible if
- ; the demon will not come within one square of another tunnel.
- ; This function returns true (carry set) if a move is possible.
- ; On entry, CH contains the direction this code should test.
-
- CanMove proc
- push ax
- push bx
-
- MazeAdrs ;Put @Maze[x,y] into ax.
- mov bx, ax
-
- cmp ch, South
- jb IsNorth
- je IsSouth
- cmp ch, East
- je IsEast
-
- ; If the demon is moving west, check the blocks in the rectangle formed
- ; by Maze[x-2,y-1] to Maze[x-1,y+1] to make sure they are all wall values.
-
- mov al, byp Maze[bx-BytesPerRow-4] ;Maze[x-2, y-1]
- add al, byp Maze[bx-BytesPerRow-2] ;Maze[x-1, y-1]
- add al, byp Maze[bx-4] ;Maze[x-2, y]
- add al, byp Maze[bx-2] ;Maze[x-1, y]
- add al, byp Maze[bx+BytesPerRow-4] ;Maze[x-2, y+1]
- add al, byp Maze[bx+BytesPerRow-2] ;Maze[x-1, y+1]
- je ReturnTrue
- ReturnFalse: clc
- pop bx
- pop ax
- ret
-
-
- ; If the demon is going east, check the blocks in the rectangle formed
- ; by Maze[x+1,y-1] to Maze[x+2,y+1] to make sure they are all wall values.
-
- IsEast: mov al, byp Maze[bx-BytesPerRow+4] ;Maze[x+2, y-1]
- add al, byp Maze[bx-BytesPerRow+2] ;Maze[x+1, y-1]
- add al, byp Maze[bx+4] ;Maze[x+2, y]
- add al, byp Maze[bx+2] ;Maze[x+1, y]
- add al, byp Maze[bx+BytesPerRow+4] ;Maze[x+2, y+1]
- add al, byp Maze[bx+BytesPerRow+2] ;Maze[x+1, y+1]
- jne ReturnFalse
- ReturnTrue: stc
- pop bx
- pop ax
- ret
-
-
- ; If the demon is going north, check the blocks in the rectangle formed
- ; by Maze[x-1,y-2] to Maze[x+1,y-1] to make sure they are all wall values.
-
- IsNorth: mov al, byp Maze[bx-BytesPerRow-2] ;Maze[x-1, y-1]
- add al, byp Maze[bx-BytesPerRow*2-2];Maze[x-1, y-2]
- add al, byp Maze[bx-BytesPerRow] ;Maze[x, y-1]
- add al, byp Maze[bx-BytesPerRow*2] ;Maze[x, y-2]
- add al, byp Maze[bx-BytesPerRow+2] ;Maze[x+1, y-1]
- add al, byp Maze[bx-BytesPerRow*2+2];Maze[x+1, y-2]
- jne ReturnFalse
- stc
- pop bx
- pop ax
- ret
-
-
-
- ; If the demon is going south, check the blocks in the rectangle formed
- ; by Maze[x-1,y+2] to Maze[x+1,y+1] to make sure they are all wall values.
-
- IsSouth: mov al, byp Maze[bx+BytesPerRow-2] ;Maze[x-1, y+1]
- add al, byp Maze[bx+BytesPerRow*2-2];Maze[x-1, y+2]
- add al, byp Maze[bx+BytesPerRow] ;Maze[x, y+1]
- add al, byp Maze[bx+BytesPerRow*2] ;Maze[x, y+2]
- add al, byp Maze[bx+BytesPerRow+2] ;Maze[x+1, y+1]
- add al, byp Maze[bx+BytesPerRow*2+2];Maze[x+1, y+2]
- jne ReturnFalse
- stc
- pop bx
- pop ax
- ret
-
- CanMove endp
-
-
-
-
- ; SetDir- Changes the current direction. The maze digging algorithm has
- ; decided to change the direction of the tunnel begin dug by one
- ; of the demons. This code checks to see if we CAN change the direction,
- ; and picks a new direction if possible.
- ;
- ; If the demon is going north or south, a direction change causes the demon
- ; to go east or west. Likewise, if the demon is going east or west, a
- ; direction change forces it to go north or south. If the demon cannot
- ; change directions (because it cannot move in the new direction for one
- ; reason or another), SetDir returns without doing anything. If a direction
- ; change is possible, then SetDir selects a new direction. If there is only
- ; one possible new direction, the demon is sent off in that direction.
- ; If the demon could move off in one of two different directions, SetDir
- ; "flips a coin" to choose one of the two new directions.
- ;
- ; This function returns the new direction in al.
-
- SetDir proc near
-
- test cl, 10b ;See if north/south
- je IsNS ; or east/west direction.
-
- ; We're going east or west. If we can move EITHER north or south from
- ; this point, randomly choose one of the directions. If we can only
- ; move one way or the other, choose that direction. If we can't go either
- ; way, return without changing the direction.
-
- mov ch, North ;See if we can move north
- call CanMove
- jnc NotNorth
- mov ch, South ;See if we can move south
- call CanMove
- jnc DoNorth
- call RandNum ;Get a random direction
- and ax, 1 ;Make it north or south.
- ret
-
- DoNorth: mov ax, North
- ret
-
- NotNorth: mov ch, South
- call CanMove
- jnc TryReverse
- DoSouth: mov ax, South
- ret
-
-
-
- ; If the demon is moving north or south, choose a new direction of east
- ; or west, if possible.
-
- IsNS: mov ch, East ;See if we can move East
- call CanMove
- jnc NotEast
- mov ch, West ;See if we can move West
- call CanMove
- jnc DoEast
- call RandNum ;Get a random direction
- and ax, 1b ;Make it East or West
- or al, 10b
- ret
-
- DoEast: mov ax, East
- ret
-
- DoWest: mov ax, West
- ret
-
- NotEast: mov ch, West
- call CanMove
- jc DoWest
-
- ; Gee, we can't switch to a perpendicular direction, see if we can
- ; turn around.
-
- TryReverse: mov ch, cl
- xor ch, 1
- call CanMove
- jc ReverseDir
-
- ; If we can't turn around (likely), then keep going in the same direction.
-
- mov ah, 0
- mov al, cl ;Stay in same direction.
- ret
-
- ; Otherwise reverse direction down here.
-
- ReverseDir: mov ah, 0
- mov al, cl
- xor al, 1
- ret
- SetDir endp
-
-
-
- ; Stuck- This function checks to see if a demon is stuck and cannot
- ; move in any direction. It returns true if the demon is
- ; stuck and needs to be killed.
-
- Stuck proc near
- mov ch, North
- call CanMove
- jc NotStuck
- mov ch, South
- call CanMove
- jc NotStuck
- mov ch, East
- call CanMove
- jc NotStuck
- mov ch, West
- call CanMove
- NotStuck: ret
- Stuck endp
-
-
-
- ; NextDemon- Searches through the demon list to find the next available
- ; active demon. Return a pointer to this guy in es:di.
-
- NextDemon proc near
- push ax
-
- NDLoop: inc DemonIndex ;Move on to next demon,
- and DemonIndex, ModDemons ; MOD MaxDemons.
- mov al, size pcb ;Compute index into
- mul DemonIndex ; DemonList.
- mov di, ax ;See if the demon at this
- add di, offset DemonList ; offset is active.
- cmp byp [di].pcb.NextProc, 0
- je NDLoop
-
- mov ax, ds
- mov es, ax
- pop ax
- ret
- NextDemon endp
-
-
-
- ; Dig- This is the demon process.
- ; It moves the demon one position (if possible) in its current
- ; direction. After moving one position forward, there is
- ; a 25% chance that this guy will change its direction; there
- ; is a 25% chance this demon will spawn a child process to
- ; dig off in a perpendicular direction.
-
- Dig proc near
-
- ; See if the current demon is stuck. If the demon is stuck, then we've
- ; go to remove it from the demon list. If it is not stuck, then have it
- ; continue digging. If it is stuck and this is the last active demon,
- ; then return control to the main program.
-
- call Stuck
- jc NotStuck
-
- ; Okay, kill the current demon.
- ; Note: this will never kill the last demon because we have the timer
- ; process running. The timer process is the one that always stops
- ; the program.
-
- dec DemonCnt
-
- ; Since the count is not zero, there must be more demons in the demon
- ; list. Free the stack space associated with the current demon and
- ; then search out the next active demon and have at it.
-
- MoreDemons: mov al, size pcb
- mul DemonIndex
- mov bx, ax
-
- ; Free the stack space associated with this process. Note this code is
- ; naughty. It assumes the stack is allocated with the Standard Library
- ; malloc routine that always produces a base address of 8.
-
- mov es, DemonList[bx].regss
- mov di, 8 ;Cheating!
- free
-
- ; Mark the demon entry for this guy as unused.
-
- mov byp DemonList[bx].NextProc, 0 ;Mark as unused.
-
-
- ; Okay, locate the next active demon in the list.
-
- FndNxtDmn: call NextDemon
- cocall ;Never returns
-
-
-
-
- ; If the demon is not stuck, then continue digging away.
-
- NotStuck: mov ch, cl
- call CanMove
- jnc DontMove
-
- ; If we can move, then adjust the demon's coordinates appropriately:
-
- cmp cl, South
- jb MoveNorth
- je MoveSouth
- cmp cl, East
- jne MoveWest
-
- ; Moving East:
-
- inc dl
- jmp MoveDone
-
- MoveWest: dec dl
- jmp MoveDone
-
- MoveNorth: dec dh
- jmp MoveDone
-
- MoveSouth: inc dh
-
- ; Okay, store a NoWall value at this entry in the maze and output a NoWall
- ; character to the screen (if writing data to the screen).
-
- MoveDone: MazeAdrs
- mov bx, ax
- mov Maze[bx], NoWall
-
- ifdef ToScreen
- ScrnAdrs
- mov bx, ax
- push es
- mov ax, ScreenSeg
- mov es, ax
- mov word ptr es:[bx], NoWallChar
- pop es
- endif
-
- ; Before leaving, see if this demon shouldn't change direction.
-
- DontMove: call RandNum
- and al, 11b ;25% chance result is zero.
- jne NoChangeDir
- call SetDir
- mov cl, al
-
- NoChangeDir:
-
-
- ; Also, see if this demon should spawn a child process
-
- call RandNum
- and al, 11b ;Give it a 25% chance.
- jne NoSpawn
-
- ; Okay, see if it's possible to spawn a new process at this point:
-
- call CanStart
- jnc NoSpawn
-
- ; See if we've already got MaxDemons active:
-
- cmp DemonCnt, MaxDemons
- jae NoSpawn
-
- inc DemonCnt ;Add another demon.
-
-
- ; Okay, create a new demon and add him to the list.
-
- push dx ;Save cur demon info.
- push cx
-
- ; Locate a free slot for this demon
-
- lea si, DemonList- size pcb
- FindSlot: add si, size pcb
- cmp byp [si].pcb.NextProc, 0
- jne FindSlot
-
-
- ; Allocate some stack space for the new demon.
-
- mov cx, 256 ;256 byte stack.
- malloc
-
- ; Set up the stack pointer for this guy:
-
- add di, 248 ;Point stack at end.
- mov [si].pcb.regss, es
- mov [si].pcb.regsp, di
-
- ; Set up the execution address for this guy:
-
- mov [si].pcb.regcs, cs
- mov [si].pcb.regip, offset Dig
-
- ; Initial coordinates and direction for this guy:
-
- mov [si].pcb.regdx, dx
-
- ; Select a direction for this guy.
-
- pop cx ;Retrieve direction.
- push cx
-
- call SetDir
- mov ah, 0
- mov [si].pcb.regcx, ax
-
- ; Set up other misc junk:
-
- mov [si].pcb.regds, seg dseg
- sti
- pushf
- pop [si].pcb.regflags
- mov byp [si].pcb.NextProc, 1 ;Mark active.
-
-
- ; Restore current process' parameters
-
- pop cx ;Restore current demon.
- pop dx
-
- NoSpawn:
-
- ; Okay, with all of the above done, it's time to pass control on to a new
- ; digger. The following cocall passes control to the next digger in the
- ; DemonList.
-
- GetNextDmn: call NextDemon
-
- ; Okay, we've got a pointer to the next demon in the list (might be the
- ; same demon if there's only one), pass control to that demon.
-
- cocall
- jmp Dig
- Dig endp
-
-
- ; TimerDemon- This demon introduces a 1/18th second delay between
- ; each cycle in the demon list. This slows down the
- ; maze generation so you can see the maze being built
- ; (which makes the program more interesting to watch).
-
- TimerDemon proc near
- push es
- push ax
-
- mov ax, 40h ;BIOS variable area
- mov es, ax
- mov ax, es:[6Ch] ;BIOS timer location
- Wait4Change: cmp ax, es:[6Ch] ;BIOS changes this every
- je Wait4Change ; 1/18th second.
-
- cmp DemonCnt, 1
- je QuitProgram
- pop es
- pop ax
- call NextDemon
- cocall
- jmp TimerDemon
-
- QuitProgram: cocall MainPCB ;Quit the program
- TimerDemon endp
-
-
-
-
- ; What good is a maze generator program if it cannot solve the mazes it
- ; creates? SolveMaze finds the solution (if any) for this maze. It marks
- ; the solution path and the paths it tried, but failed on.
- ;
- ; function solvemaze(x,y:integer):boolean
-
- sm_X textequ <[bp+6]>
- sm_Y textequ <[bp+4]>
-
- SolveMaze proc near
- push bp
- mov bp, sp
-
- ; See if we've just solved the maze:
-
- cmp byte ptr sm_X, EndX
- jne NotSolved
- cmp byte ptr sm_Y, EndY
- jne NotSolved
- mov ax, 1 ;Return true.
- pop bp
- ret 4
-
- ; See if moving to this spot was an illegal move. There will be
- ; a NoWall value at this cell in the maze if the move is legal.
-
- NotSolved: mov dl, sm_X
- mov dh, sm_Y
- MazeAdrs
- mov bx, ax
- cmp Maze[bx], NoWall
- je MoveOK
- mov ax, 0 ;Return failure
- pop bp
- ret 4
-
- ; Well, it is possible to move to this point, so place an appropriate
- ; value on the screen and keep searching for the solution.
-
- MoveOK: mov Maze[bx], Visited
-
- ifdef ToScreen
- push es ;Write a "VisitChar"
- ScrnAdrs ; character to the
- mov bx, ax ; screen at this X,Y
- mov ax, ScreenSeg ; position.
- mov es, ax
- mov word ptr es:[bx], VisitChar
- pop es
- endif
-
- ; Recusively call SolveMaze until we get a solution. Just call SolveMaze
- ; for the four possible directions (up, down, left, right) we could go.
- ; Since we've left "Visited" values in the Maze, we will not accidentally
- ; search back through the path we've already travelled. Furthermore, if
- ; we cannot go in one of the four directions, SolveMaze will catch this
- ; immediately upon entry (see the code at the start of this routine).
-
- mov ax, sm_X ;Try the path at location
- dec ax ; (X-1, Y)
- push ax
- push sm_Y
- call SolveMaze
- test ax, ax ;Solution?
- jne Solved
-
- push sm_X ;Try the path at location
- mov ax, sm_Y ; (X, Y-1)
- dec ax
- push ax
- call SolveMaze
- test ax, ax ;Solution?
- jne Solved
-
- mov ax, sm_X ;Try the path at location
- inc ax ; (X+1, Y)
- push ax
- push sm_Y
- call SolveMaze
- test ax, ax ;Solution?
- jne Solved
-
- push sm_X ;Try the path at location
- mov ax, sm_Y ; (X, Y+1)
- inc ax
- push ax
- call SolveMaze
- test ax, ax ;Solution?
- jne Solved
- pop bp
- ret 4
-
- Solved:
- ifdef ToScreen ;Draw return path.
- push es
- mov dl, sm_X
- mov dh, sm_Y
- ScrnAdrs
- mov bx, ax
- mov ax, ScreenSeg
- mov es, ax
- mov word ptr es:[bx], PathChar
- pop es
- mov ax, 1 ;Return true
- endif
-
- pop bp
- ret 4
- SolveMaze endp
-
-
-
- ; Here's the main program that drives the whole thing:
-
- Main proc
- mov ax, dseg
- mov ds, ax
- mov es, ax
- meminit
-
-
- call Init ;Initialize maze stuff.
- lesi MainPCB ;Initialize coroutine
- coinit ; package.
-
- ; Create the first demon.
- ; Set up the stack pointer for this guy:
-
- mov cx, 256
- malloc
- add di, 248
- mov DemonList.regsp, di
- mov DemonList.regss, es
-
- ; Set up the execution address for this guy:
-
- mov DemonList.regcs, cs
- mov DemonList.regip, offset Dig
-
- ; Initial coordinates and direction for this guy:
-
- mov cx, East ;Start off going east.
- mov dh, StartY
- mov dl, StartX
- mov DemonList.regcx, cx
- mov DemonList.regdx, dx
-
- ; Set up other misc junk:
-
- mov DemonList.regds, seg dseg
- sti
- pushf
- pop DemonList.regflags
- mov byp DemonList.NextProc, 1 ;Demon is "active".
- inc DemonCnt
- mov DemonIndex, 0
-
-
-
-
- ; Set up the Timer demon:
-
- mov DemonList.regsp+(size pcb), offset EndTimerStk
- mov DemonList.regss+(size pcb), ss
-
- ; Set up the execution address for this guy:
-
- mov DemonList.regcs+(size pcb), cs
- mov DemonList.regip+(size pcb), offset TimerDemon
-
- ; Set up other misc junk:
-
- mov DemonList.regds+(size pcb), seg dseg
- sti
- pushf
- pop DemonList.regflags+(size pcb)
- mov byp DemonList.NextProc+(size pcb), 1
- inc DemonCnt
-
- ; Start the ball rolling.
-
- mov ax, ds
- mov es, ax
- lea di, DemonList
- cocall
-
- ; Wait for the user to press a key before solving the maze:
-
- getc
-
- mov ax, StartX
- push ax
- mov ax, StartY
- push ax
- call SolveMaze
-
- ; Wait for another keystroke before quitting:
-
- getc
-
- mov ax, 3 ;Clear screen and reset video mode.
- int 10h
-
- Quit: ExitPgm ;DOS macro to quit program.
- Main endp
-
- cseg ends
-
- sseg segment para stack 'stack'
-
- ; Stack for the timer demon we create (we'll allocate the other
- ; stacks dynamically).
-
- TimerStk byte 256 dup (?)
- EndTimerStk word ?
-
-
- ; Main program's stack:
-
- stk byte 512 dup (?)
- sseg ends
-
- zzzzzzseg segment para public 'zzzzzz'
- LastBytes db 16 dup (?)
- zzzzzzseg ends
- end Main
-